package action.chap7;

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.stream.LongStream;

/**
 *  分之合并框架
 *  、+++++++++++++
 *  分支/合并框架的目的是以递归方式将可以并行的任务拆分成更小的任务，然后将每个子任务的结果合并起来生成整体结果。
 *
 *  循环的递归的
 */
public class ForkJoinSumCalculator extends RecursiveTask<Long> {

    private final long[] numbers;//要求和的数组
    private final int start; //子任务处理的数组的起始
    private final int end;//和终止位置

    /**
     * 私有构造函数用于以递归方式为主任务创建子任务
     *
     * @param numbers
     * @param start
     * @param end
     */
    public ForkJoinSumCalculator(long[] numbers, int start, int end) {
        this.numbers = numbers;
        this.start = start;
        this.end = end;
    }


    //覆盖RecursiveTask抽象方法
    @Override
    protected Long compute() {
        //该任务负责求和的部分的大小
        int length = end - start;
        int THRESHOLD = 1000;
        //如果大小小于或等于阈值，顺序计算结果
        if (length <= THRESHOLD) {

            return computeSequentially();
        }
        //创建一个子任务来为数组的前一半求和
        ForkJoinSumCalculator leftTask =
                new ForkJoinSumCalculator(numbers, start, start + length / 2);
        //利用另一个ForkJoinPool线程异步执行新创建的子任务
        leftTask.fork();
        //创建一个任务为数组的后半求和
        ForkJoinSumCalculator rightTask =
                new ForkJoinSumCalculator(numbers, start + length / 2, end);
        //同步执行第二个子任务，有可能允许进一步递归划分
        Long rightResult = rightTask.compute();
        //读取第一个子任务的结果，如果尚未完成就等待
        Long leftResult = leftTask.join();
        // 使用fork join框架
        return leftResult + rightResult;
    }

    /**
     * 在子任务不再可分时计算结果的简单算法
     * @return
     */
    private long computeSequentially() {
        long sum = 0;
        for (int i = start; i < end; i++) {
            sum += numbers[i];
        }
        return sum;
    }

    public static long forkJoinSum(long n) {
        long[] numbers = LongStream.rangeClosed(1, n).toArray();
        ForkJoinTask<Long> task = new ForkJoinSumCalculator(numbers,1,100);
        return new ForkJoinPool().invoke(task);
    }
}
